package cc.yaomm.common.basic.base;

import cc.yaomm.common.basic.aspect.RequestLogAspect;
import cc.yaomm.common.basic.util.SpringContextUtils;
import cc.yaomm.common.basic.web.filter.RequestBodyFilter;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
import org.springframework.boot.task.TaskExecutorBuilder;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;

public class SuperApplication {

    @Bean
    public RequestLogAspect requestLogAspect() {
        return new RequestLogAspect();
    }

    @Bean
    public SuperExceptionHandler defaultExceptionHandler() {
        return new SuperExceptionHandler();
    }

    @Bean
    public SpringContextUtils springContextUtils() {
        return new SpringContextUtils();
    }

    /**
     * <p>@Description : 解决请求BODY只能读取一次的问题</p>
     * <p>@Author : QiLin.Xing </p>
     * <p>@Date : 2018/8/1 9:23 </p>
     */
    @Bean
    public FilterRegistrationBean requestBodyFilter() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new RequestBodyFilter());
        registrationBean.addUrlPatterns("/*");
        registrationBean.setName("requestBodyFilter");
        registrationBean.setOrder(1);
        return registrationBean;
    }

//    /**
//     * <p>@Description : 解决跨域问题</p>
//     * <p>@Author : QiLin.Xing </p>
//     * <p>@Date : 2018/8/11 9:13 </p>
//     */
//    @Bean
//    public CorsFilter corsFilter() {
//        CorsConfiguration configuration = new CorsConfiguration();
//        configuration.addAllowedOrigin("*");
//        configuration.addAllowedHeader("*");
//        configuration.addAllowedMethod("*");
//
//        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
//        source.registerCorsConfiguration("/**", configuration);
//        return new CorsFilter(source);
//    }

    /**
     * <p>@Description : 处理响应JSON时，字符串为null不序列化</p>
     * <p>@Author : QiLin.Xing </p>
     * <p>@Date : 2019/3/16 15:10 </p>
     * <p>
     * 备注：
     * 1.不序列化值为Null的属性
     * 2.默认将数组、collection、Map等类型的Null设为空对象
     * 3.反序列化时清空字符串前后空格
     */
    @Bean
    @Primary
//    @ConditionalOnMissingBean(ObjectMapper.class)  必须使用自定义扩展的ObjectMapper
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
        //通过该方法对mapper对象进行设置，所有序列化的对象都将按改规则进行系列化
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();

        //将Null类型的数组、collection、Map等转为空对象
        objectMapper.setSerializerFactory(objectMapper.getSerializerFactory().withSerializerModifier(new BeanSerializerModifier() {
            @Override
            public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
                for (BeanPropertyWriter beanProperty : beanProperties) {
                    Class clazz = beanProperty.getType().getRawClass();
                    //判断是否为数组，list,set
                    if (clazz.isArray() || Collection.class.isAssignableFrom(clazz)) {
                        beanProperty.assignNullSerializer(new JsonSerializer<Object>() {
                            @Override
                            public void serialize(Object value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                                jsonGenerator.writeStartArray();
                                jsonGenerator.writeEndArray();
                            }
                        });
                    } else if (Map.class.isAssignableFrom(clazz)) {
                        beanProperty.assignNullSerializer(new JsonSerializer<Object>() {
                            @Override
                            public void serialize(Object value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                                jsonGenerator.writeStartObject();
                                jsonGenerator.writeEndObject();
                            }
                        });
                    }
                }
                return beanProperties;
            }
        }));

        //设置全局日期转换
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

        //反序列化时清空字符串前后空格
        SimpleModule module = new SimpleModule();
        module.addDeserializer(String.class, new StdDeserializer<String>(String.class) {
            @Override
            public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
                //清空字符串前后的空格
                return jsonParser.getText() != null ? jsonParser.getText().trim() : null;
            }
        });
        objectMapper.registerModule(module);

        //Include.ALWAYS 默认
        //Include.NON_DEFAULT 属性为默认值不序列化
        //Include.NON_EMPTY 属性为 空（“”） 或者为 NULL 都不序列化
        //Include.NON_NULL 属性为NULL 不序列化
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

//        objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
        //字符串转枚举，没有找到枚举转为Null
        objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);

        return objectMapper;
    }

    /**
     * <p>@Description : 初始化全局线程池 </p>
     * <p>@Author : QiLin.Xing </p>
     * <p>@Date : 2021/10/20 9:56 </p>
     * <p>
     * 注意：重新此方法的目的在于Springboot提供的无法自定义拒绝策略，并它默认的拒绝策略是抛出拒绝执行的异常，在实际业务中一般不会这样处理
     * 配置参数异常遵从springboot提供的配置方式，参考TaskExecutionProperties，建议必须配置
     *
     * @see org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration
     * @see org.springframework.boot.autoconfigure.task.TaskExecutionProperties
     */
    @Lazy
    @Bean(name = {TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME,
            AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME})
    public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
        ThreadPoolTaskExecutor taskExecutor = builder.build();
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); //无线程可用时拒绝策略，采用调用线程执行
        return taskExecutor;
    }
}
